 /*******************************************************************************
  * Copyright (c) 2004, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM - Initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.progress;

 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
 import org.eclipse.core.runtime.jobs.IJobChangeListener;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.progress.IProgressConstants;
 import org.eclipse.ui.progress.WorkbenchJob;

 /**
  * The ProgressMonitorFocusJobDialog is a dialog that shows progress for a
  * particular job in a modal dialog so as to give a user accustomed to a modal
  * UI a more familiar feel.
  */
 class ProgressMonitorFocusJobDialog extends ProgressMonitorJobsDialog {
     Job job;

     /**
      * Create a new instance of the receiver with progress reported on the job.
      *
      * @param parentShell
      * The shell this is parented from.
      */
     public ProgressMonitorFocusJobDialog(Shell parentShell) {
         super(parentShell == null ? ProgressManagerUtil.getNonModalShell()
                 : parentShell);
         setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE
                 | SWT.RESIZE | SWT.MODELESS);
         setCancelable(true);
         enableDetailsButton = true;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.dialogs.ProgressMonitorDialog#cancelPressed()
      */
     protected void cancelPressed() {
         job.cancel();
         super.cancelPressed();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.dialogs.ProgressMonitorDialog#configureShell(org.eclipse.swt.widgets.Shell)
      */
     protected void configureShell(Shell shell) {
         super.configureShell(shell);
         shell.setText(job.getName());

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
      */
     protected void createButtonsForButtonBar(Composite parent) {
         Button runInWorkspace = createButton(
                 parent,
                 IDialogConstants.CLOSE_ID,
                 ProgressMessages.ProgressMonitorFocusJobDialog_RunInBackgroundButton,
                 true);
         runInWorkspace.addSelectionListener(new SelectionAdapter() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
              */
             public void widgetSelected(SelectionEvent e) {
                 Rectangle shellPosition = getShell().getBounds();
                 job.setProperty(IProgressConstants.PROPERTY_IN_DIALOG,
                         Boolean.FALSE);
                 finishedRun();
                 ProgressManagerUtil.animateDown(shellPosition);
             }
         });
         runInWorkspace.setCursor(arrowCursor);

         cancel = createButton(parent, IDialogConstants.CANCEL_ID,
                 IDialogConstants.CANCEL_LABEL, false);
         cancel.setCursor(arrowCursor);

         createDetailsButton(parent);
     }

     /**
      * Returns a listener that will close the dialog when the job completes.
      *
      * @return IJobChangeListener
      */
     private IJobChangeListener createCloseListener() {
         return new JobChangeAdapter() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.jobs.IJobChangeListener#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
              */
             public void done(IJobChangeEvent event) {
                 // first of all, make sure this listener is removed
 event.getJob().removeJobChangeListener(this);
                 if (!PlatformUI.isWorkbenchRunning()) {
                     return;
                 }
                 // nothing to do if the dialog is already closed
 if (getShell() == null) {
                     return;
                 }
                 WorkbenchJob closeJob = new WorkbenchJob(
                         ProgressMessages.ProgressMonitorFocusJobDialog_CLoseDialogJob) {
                     /*
                      * (non-Javadoc)
                      *
                      * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
                      */
                     public IStatus runInUIThread(IProgressMonitor monitor) {
                         Shell currentShell = getShell();
                         if (currentShell == null || currentShell.isDisposed()) {
                             return Status.CANCEL_STATUS;
                         }
                         finishedRun();
                         return Status.OK_STATUS;
                     }
                 };
                 closeJob.setSystem(true);
                 closeJob.schedule();
             }
         };
     }

     /**
      * Return the ProgressMonitorWithBlocking for the receiver.
      *
      * @return IProgressMonitorWithBlocking
      */
     private IProgressMonitorWithBlocking getBlockingProgressMonitor() {
         return new IProgressMonitorWithBlocking() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#beginTask(java.lang.String,
              * int)
              */
             public void beginTask(String name, int totalWork) {
                 final String finalName = name;
                 final int finalWork = totalWork;
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         getProgressMonitor().beginTask(finalName, finalWork);
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
              */
             public void clearBlocked() {
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         ((IProgressMonitorWithBlocking) getProgressMonitor())
                                 .clearBlocked();
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#done()
              */
             public void done() {
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         getProgressMonitor().done();
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#internalWorked(double)
              */
             public void internalWorked(double work) {
                 final double finalWork = work;
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         getProgressMonitor().internalWorked(finalWork);
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#isCanceled()
              */
             public boolean isCanceled() {
                 return getProgressMonitor().isCanceled();
             }

             /**
              * Run the runnable as an asyncExec if we are already open.
              *
              * @param runnable
              */
             private void runAsync(final Runnable runnable) {

                 if (alreadyClosed) {
                     return;
                 }
                 Shell currentShell = getShell();

                 Display display;
                 if (currentShell == null) {
                     display = Display.getDefault();
                 } else {
                     if (currentShell.isDisposed())// Don't bother if it has
 // been closed
 return;
                     display = currentShell.getDisplay();
                 }

                 display.asyncExec(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         if (alreadyClosed) {
                             return;// Check again as the async may come too
 // late
 }
                         Shell shell = getShell();
                         if (shell != null && shell.isDisposed())
                             return;

                         runnable.run();
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
              */
             public void setBlocked(IStatus reason) {
                 final IStatus finalReason = reason;
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         ((IProgressMonitorWithBlocking) getProgressMonitor())
                                 .setBlocked(finalReason);
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#setCanceled(boolean)
              */
             public void setCanceled(boolean value) {
                 // Just a listener - doesn't matter.
 }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
              */
             public void setTaskName(String name) {
                 final String finalName = name;
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         getProgressMonitor().setTaskName(finalName);
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#subTask(java.lang.String)
              */
             public void subTask(String name) {
                 final String finalName = name;
                 runAsync(new Runnable () {
                     /*
                      * (non-Javadoc)
                      *
                      * @see java.lang.Runnable#run()
                      */
                     public void run() {
                         getProgressMonitor().subTask(finalName);
                     }
                 });
             }

             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.core.runtime.IProgressMonitor#worked(int)
              */
             public void worked(int work) {
                 internalWorked(work);
             }
         };
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.window.Window#open()
      */
     public int open() {
         int result = super.open();

         // add a listener that will close the dialog when the job completes.
 IJobChangeListener listener = createCloseListener();
         job.addJobChangeListener(listener);
         if (job.getState() == Job.NONE) {
             // if the job completed before we had a chance to add
 // the listener, just remove the listener and return
 job.removeJobChangeListener(listener);
             finishedRun();
             cleanUpFinishedJob();
         }

         return result;
     }

     /**
      * Opens this dialog for the duration that the given job is running.
      *
      * @param jobToWatch
      * @param originatingShell
      * The shell this request was created from. Do not block on this
      * shell.
      */
     public void show(Job jobToWatch, final Shell originatingShell) {
         job = jobToWatch;
         // after the dialog is opened we can get access to its monitor
 job.setProperty(IProgressConstants.PROPERTY_IN_DIALOG, Boolean.TRUE);

         ProgressManager.getInstance().progressFor(job).addProgressListener(
                 getBlockingProgressMonitor());

         setOpenOnRun(false);
         aboutToRun();
         // start with a quick busy indicator. Lock the UI as we
 // want to preserve modality
 BusyIndicator.showWhile(PlatformUI.getWorkbench().getDisplay(),
                 new Runnable () {
                     public void run() {
                         try {
                             Thread
                                     .sleep(ProgressManagerUtil.SHORT_OPERATION_TIME);
                         } catch (InterruptedException e) {
                             // Do not log as this is a common operation from the
 // lock listener
 }
                     }
                 });

         WorkbenchJob openJob = new WorkbenchJob(
                 ProgressMessages.ProgressMonitorFocusJobDialog_UserDialogJob) {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
              */
             public IStatus runInUIThread(IProgressMonitor monitor) {

                 // if the job is done at this point, we don't need the dialog
 if (job.getState() == Job.NONE) {
                     finishedRun();
                     cleanUpFinishedJob();
                     return Status.CANCEL_STATUS;
                 }

                 // now open the progress dialog if nothing else is
 if (!ProgressManagerUtil.safeToOpen(
                         ProgressMonitorFocusJobDialog.this, originatingShell)) {
                     return Status.CANCEL_STATUS;
                 }

                 // Do not bother if the parent is disposed
 if (getParentShell() != null && getParentShell().isDisposed()) {
                     return Status.CANCEL_STATUS;
                 }

                 open();

                 return Status.OK_STATUS;
             }
         };
         openJob.setSystem(true);
         openJob.schedule();

     }

     /**
      * The job finished before we did anything so clean up the finished
      * reference.
      */
     private void cleanUpFinishedJob() {
         ProgressManager.getInstance().checkForStaleness(job);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
      */
     protected Control createDialogArea(Composite parent) {
         Control area = super.createDialogArea(parent);
         // Give the job info as the initial details
 getProgressMonitor().setTaskName(
                 ProgressManager.getInstance().getJobInfo(this.job)
                         .getDisplayString());
         return area;
     }
 }

